using System;
using System.Text.RegularExpressions;
using protobuf.Protocol;
using UnityEngine;
using Tools;

namespace Core.Net
{
    /// <summary>
    /// 网络管理
    /// </summary>
    public partial class NetManager : MonoSingletonProvider<NetManager>, IUSocketEventListener
    {


        internal USocketGroup socket;

        /// <summary>
        /// 是否已经连接
        /// </summary>
        public bool IsConnected => socket != null && socket.IsAllSocketConnected();

        public string IP { get; private set; }
        public int Port { get; private set; }

        //服务器时间
        private DateTime _serverDateTime = DateTime.UtcNow.ToLocalTime();

        //服务器更新时间
        private int _clientUpdateServerTimeTick = NetHelper.GetEnvironmentTickCount();

        //目前服务器时间,第一次连接完成后从服务器获得
        public DateTime ServerTime => _serverDateTime.AddMilliseconds(NetHelper.GetEnvironmentTickCount() - _clientUpdateServerTimeTick);

        //接收缓冲
        private ConcurrentQueue<Frame> receiveQueue = new ConcurrentQueue<Frame>(100);
        //发送缓冲
        private ConcurrentQueue<Frame> sendQueue = new ConcurrentQueue<Frame>(100);
        //套接字事件消息队列
        private ConcurrentQueue<SocketCallBackMsgType> socketEventMsg = new ConcurrentQueue<SocketCallBackMsgType>(5);
        // 调用连接函数后连接事件的回调，调用一次后就会被清空 
        private Signal<bool> connectCallback = new Signal<bool>();

        // 客户端IP
        public string ClientIP = "";

        // 客户端端口号
        public int ClientPort = 0;

        // 总共发送数据量 
        public float TotalSendKB = 0;

        // 总共发送帧数量 
        public float TotalSendFrames = 0;

        // 总共收到数据量 
        public float TotalReceiveKB = 0;

        // 总共收到帧数量 
        public float TotalReceiveFrames = 0;

        private bool IsInitConnet = false;

        protected override void Awake()
        {
            base.Awake();
            TimerSystem?.Create();
        }

        protected override void Start()
        {
            base.Start();
        }

        public void InitConnet()
        {
            if (IsInitConnet == false)
            {
                IsInitConnet = true;
                RPC.RegisterIRPCSyncConnect();
            }
        }

        void Update()
        {
            TimerSystem?.UpdateTimer();

            // 套接字事件更新，将套接字线程转移到UI线程
            SocketEventUpdate();

            //网络数据收发更新循环
            NetUpdate();

            //网络Ping值检查
            NetPingCheck();

            //网络连接守护
            NetConnectGuard();
        }

        protected override void OnDestroy()
        {
            base.OnDestroy();
            TimerSystem?.Destroy();
            Close();
        }

        //Net更新
        void NetUpdate()
        {
            if (socket != null && IsConnected)
            {
                UnityEngine.Profiling.Profiler.BeginSample("Receive");
                //处理接收数据 
                while (receiveQueue.Count > 0)
                {
                    var frame = receiveQueue.Dequeue();
                    TotalReceiveKB += frame.FrameLen;
                    TotalReceiveFrames++;
                    ReceiveRPC(frame);
                    frame.Consumed();
                }
                UnityEngine.Profiling.Profiler.EndSample();
                UnityEngine.Profiling.Profiler.BeginSample("Send");
                //发送数据
                while (sendQueue.Count > 0)
                {
                    if (sendQueue.Count > 50 && sendQueue.Count % 50 == 0)
                    {
#if UNITY_EDITOR
                        Debug.LogWarning($"在断线情况下导致网络管理堆积了{sendQueue.Count}个数据包，这样不利于游戏性能");
#endif
                    }
                    UnityEngine.Profiling.Profiler.BeginSample("Dequeue");
                    // //拿出属于队列头的数据，但是暂时不Dequeue出
                    //  var frame = sendQueue.Peek();
                    var frame = sendQueue.Dequeue();
                    UnityEngine.Profiling.Profiler.EndSample();
                    SocketSend(frame);
                    //  //发送成功，移除队列头数据
                    // sendQueue.Dequeue();
                }
                UnityEngine.Profiling.Profiler.EndSample();
            }
        }


        protected override void OnApplicationQuit()
        {
            /*if (socket != null)
        {
            socket.isAllClose = true;
        }*/
            Close();
            base.OnApplicationQuit();
        }

        /// <summary>
        /// 连接，IP地址读取ServerConfig
        /// </summary>
        public NetAsyncResult Connect(string ip, int port, bool isAsync = true, Action<bool> callback = null)
        {
            var res = new NetAsyncResult();
            try
            {
                if (socket != null && !socket.IsAllSocketClosed()) Close();
                if (socket == null) socket = new USocketGroup();
                //初始化套接字
                if (!IsConnected) socket.AddListener(this);
                //如果已经连接则无需再次连接 
                if (socket.IsAllSocketConnected())
                {
                    res.Complete();
                    res.isSuccess = true;
                    callback?.Invoke(true);
                    return res;
                }
                Action<bool> cb = (b) =>
                {
                    res.isSuccess = b;
                    res.Complete();
                    callback?.Invoke(b);
                };
                connectCallback.AddListener(cb);
                IP = ip;
                Port = port;
                res.isSuccess = socket.Connect(IP, Port, isAsync, RPCMsgManager.TIMEOUT);
                res.Complete();
                return res;
            }
            catch (Exception e)
            {
                res.Complete();
                res.isSuccess = false;
                callback?.Invoke(false);
                Debug.LogError(e.Message);
            }
            return res;
        }

        /// <summary>
        /// 关闭网络连接
        /// TODO:添加关闭原因，来自客户端的调用关闭则关闭原先的守护等配置。因为服务器意外的关闭则不需要关闭守护等
        /// </summary>
        public void Close()
        {
            if (socket == null) return;
            socket.RemoveListener(this);
            socket.AllClose();
        }

        private float _preReconnectingTime = 0;

        //断线重连
        public void Reconnect(Action<bool, protobuf.Account.OnlineModel> callback = null, RPCAsyncResult exceptClearRpcResult = null)
        {
            if (Time.unscaledTime - _preReconnectingTime < 0.1f)
            {
#if UNITY_EDITOR
                Debug.LogError("访问速度过快");
#endif
            }
            if (!IsConnected) //避免快速多次调用
            {
                _preReconnectingTime = Time.unscaledTime;
                //重连
                Connect(IP, Port, true, success =>
                {
                    if (success)
                    {
                        _preReconnectingTime = Time.unscaledTime;
                        var excptRpc = exceptClearRpcResult;
                        //清除发送队列数据
                        RPC.ClearRPC(excptRpc);
                        var result = RPC.DoNetReconnect();
                        //result为空表明没有Token数据，无需发送重连数据
                        if (result == null)
                        {
                            callback?.Invoke(true, null);
                        }
                        else
                        {
                            result.OnFinished.AddListener(res =>
                            {
                                var resp = (protobuf.Account.RespLoginTokenModel) res.resultParam0;
                                if (res.errorType == RPCErrorType.None)
                                {
                                    //刷新登录数据
                                    //GameLogicManager.inst.InitFromServer(resp.Data);
                                    callback?.Invoke(true, resp.Data);
                                }
                                else if (res.errorType != RPCErrorType.ClearRPC) //多次调用时候ClearRPC会和上面调用冲突
                                {
                                    callback?.Invoke(false, null);
                                    Close();
                                }
                            });
                        }
                    }
                    else
                    {
                        callback?.Invoke(false, null);
                        Close();
                    }
                });
            }
        }

        /// <summary>
        /// 断开当前网络连接，清除网络连接数据
        /// </summary>
        public void Clear()
        {
            //接收缓冲
            receiveQueue = new ConcurrentQueue<Frame>(100);
            //发送缓冲
            sendQueue = new ConcurrentQueue<Frame>(100);
            //ACK队列，存放MSGID
            ackQueue = new ConcurrentHashSet<int>();
            //套接字事件消息队列
            socketEventMsg = new ConcurrentQueue<SocketCallBackMsgType>(5);
            // 调用连接函数后连接事件的回调，调用一次后就会被清空 
            connectCallback = new Signal<bool>();
            
            //RPC.Account.LoginOut(null);
            Close();
            //如果RPC还有请求，告知停止
            TimerSystem.Destroy();
            RPC.ClearLoginToken();
            RPC.ClearRPC();
            RPC = new RPCMsgManager();
            socket = null;
        }

        #region RPC

        //将数据经过套接字发送出去,发送前需要判断是否套接字可以发送，否则发送的数据包会被丢弃
        void SocketSend(Frame frame)
        {
            if (frame == null) return;
            {
                if (socket != null)
                {
                    UnityEngine.Profiling.Profiler.BeginSample("SocketSend");
                    socket.Send(frame);
                    UnityEngine.Profiling.Profiler.EndSample();
#if UNITY_EDITOR
                    //过滤Sync LOG，不然太多了
                    if (!Regex.IsMatch(((ProtoType) frame.RPCID).ToString(), "Sync|ACK|Ping"))
                    {
                        var waitTime = NetHelper.GetEnvironmentTickCount() - frame.DealTimeTick;
                        msgIDTime[frame.MSGID % msgIDTime.Length] = NetHelper.GetEnvironmentTickCount();
                        Debug.LogWarning($"发送RPC请求rpcID={((ProtoType) frame.RPCID).ToString().ToString()}，msgID={frame.MSGID},等待时长:{waitTime}, 当前时间：{ServerTime.ToLocalTime().ToString()}");
                    }
#endif
                    TotalSendKB += frame.FrameLen;
                    TotalSendFrames++;
                }
            }
        }

        #endregion

        public void USocketEvent_OnMessage(USocketGroup us, Frame reader)
        {
            if (reader.MSGID > 0)
            {
                var hasAck = ackQueue.Remove(reader.MSGID);
                if (!hasAck) //不存在可能丢请求了
                {
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                    Debug.LogWarning($"ACKLose RPCID:{((ProtoType) reader.RPCID).ToString()} MSGID:{reader.MSGID}");
#endif
                }
            }
            receiveQueue.Enqueue(reader);
        }

        #region 连接异常事件处理

        public void USocketEvent_OnClose(USocketGroup us, bool fromRemote)
        {
#if UNITY_EDITOR
            Debug.LogWarning("连接被关闭，由于" + (fromRemote ? "服务器原因" : "客户端原因\n" + new System.Diagnostics.StackTrace()));
#endif
            socketEventMsg.Enqueue(SocketCallBackMsgType.OnClose);
        }

        public void USocketEvent_OnIdle(USocketGroup us)
        {
#if UNITY_EDITOR || DEVELOPMENT_BUILD
            Debug.LogError("连接超时");
#endif
            socketEventMsg.Enqueue(SocketCallBackMsgType.OnIdle);
        }

        public void USocketEvent_OnConnect(USocketGroup us, bool success)
        {
#if UNITY_EDITOR || DEVELOPMENT_BUILD
            Debug.Log("连接状态：" + success);
#endif
            if (success && us.IsAllSocketConnected())
            {
                socket = us;
            }
            socketEventMsg.Enqueue(SocketCallBackMsgType.OnConnect);
        }

        public void USocketEvent_OnTimeOut(USocketGroup us)
        {
            
            socketEventMsg.Enqueue(SocketCallBackMsgType.OnTimeOut);
        }

        /// <summary>
        /// 严重通讯错误
        /// </summary>
        /// <param name="us"></param>
        /// <param name="err"></param>
        public void USocketEvent_OnError(USocketGroup us, string err)
        {
#if UNITY_EDITOR || DEVELOPMENT_BUILD
            Debug.LogError($"连接错误:{err}");
#endif
            socketEventMsg.Enqueue(SocketCallBackMsgType.OnError);
            if (us == socket)
                Close();
        }

        // 在UI线程完成套接字事件更新
        void SocketEventUpdate()
        {
            //将套接字提醒事件转换到UI线程
            while (socketEventMsg.Count > 0)
            {
                var msg = socketEventMsg.Dequeue();
                if (msg == SocketCallBackMsgType.OnClose)
                {
                    OnClose.Dispatch();
                }
                if (msg == SocketCallBackMsgType.OnConnect)
                {

                    //通知回掉函数，并清空一次性函数库
                    var oldCb = connectCallback;
                    oldCb.Dispatch(IsConnected);
                    connectCallback = new Signal<bool>();
                    OnConnect.Dispatch(IsConnected);
                }
                if (msg == SocketCallBackMsgType.OnIdle)
                {
                    OnIdle.Dispatch();
                }
                if (msg == SocketCallBackMsgType.OnTimeOut)
                {
                    OnTimeOut.Dispatch();
                }
                if (msg == SocketCallBackMsgType.OnError)
                {
                    OnError.Dispatch();
                    ShowReconnectFailedBackToLogin();
                }
            }
        }

        #endregion

        /// <summary>
        /// 套接字回调消息，因为在不同线程，所以需要消息方式
        /// </summary>
        public enum SocketCallBackMsgType
        {
            OnConnect = 1,
            OnIdle = 2,
            OnClose = 3,
            OnTimeOut = 4,
            OnError = 5
        }
        
        /// <summary>
        /// 连接事件的回调
        /// </summary>
        public readonly Signal<bool> OnConnect = new Signal<bool>();

        /// <summary>
        /// 长时间心跳包没有反应
        /// </summary>
        public readonly Signal OnIdle = new Signal();

        /// <summary>
        /// 当关闭链接
        /// </summary>
        public readonly Signal OnClose = new Signal();
        
        /// <summary>
        /// 出现错误
        /// </summary>
        public readonly Signal OnError = new Signal();
        
        /// <summary>
        /// 连接超时
        /// </summary>
        public readonly Signal OnTimeOut = new Signal();

        /// <summary>
        /// 当网络状态变化
        /// </summary>
        public readonly Signal<ePingState> OnPingStateChanged = new Signal<ePingState>();

        

    }
}